//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#ifndef HYDRA_LOCKS_ARRAYLOCK_H
#define HYDRA_LOCKS_ARRAYLOCK_H 1

#include "hydra/backoff/Backoff.h"
#include "hydra/thread/ThreadLocal.h"

namespace Hydra
{
	template<unsigned int N>
	class ArrayLock
	{
	public:
		ArrayLock()
		{
			assert(((N-1)&N) == 0);	//N must be a power of two, otherwise when m_tail wraps the slot won't wrap with it
			m_tail = 0;
			m_nodes[0].m_flag = true;
			for( unsigned int i=1; i<N; ++i )
				m_nodes[i].m_flag = false;
		}
		void lock()
		{
			Backoff backoff;
			unsigned int slot = m_tail.getAndAdd(1) % N;
			*m_slot = slot;
			while( !m_nodes[slot].m_flag )	{ backoff.wait(); }
		}
		void unlock()
		{
			unsigned int slot = *m_slot;
			m_nodes[slot].m_flag = false;
			m_nodes[(slot+1)%N].m_flag = true;
		}
	private:
		Atomic<unsigned int> m_tail;
		struct Node
		{
			Atomic<bool> m_flag;
			char m_pad[128-sizeof(Atomic<bool>)];
		};
		Node m_nodes[N];
		ThreadLocal<unsigned int> m_slot;
	};
}

#endif
